//////////////////////////////////////////////////////////////////////////////
//	Copyright 2011 Alex Leffelman
//	
//	Licensed under the Apache License, Version 2.0 (the "License");
//	you may not use this file except in compliance with the License.
//	You may obtain a copy of the License at
//	
//	http://www.apache.org/licenses/LICENSE-2.0
//	
//	Unless required by applicable law or agreed to in writing, software
//	distributed under the License is distributed on an "AS IS" BASIS,
//	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//	See the License for the specific language governing permissions and
//	limitations under the License.
//////////////////////////////////////////////////////////////////////////////

package com.zhuxulin.midi.util;

/**
 * MIDI Unit Conversions
 */
public class MidiUtil {
	/**
	 * ticks to ms
	 * @param ticks
	 * @param mpqn ms per quarter note
	 * @param resolution 
	 * @return
	 */
	public static long ticksToMs(long ticks, int mpqn, int resolution) {
		return ((ticks * mpqn) / resolution)/1000;
	}
	public static long ticksToMs(long ticks, float bpm, int resolution) {
		return ticksToMs(ticks, bpmToMpqn(bpm), resolution);
	}
	/**
	 * ms*(tick/qt)/(ms/qt)=tick
	 * @param ms
	 * @param mpqn ms/qt
	 * @param ppq tick/qt
	 * @return
	 */
	public static double msToTicks(long ms, int mpqn, int ppq) {
		return ((ms*1000.0)*ppq)/mpqn;
	}
	/**
	 * 
	 * @param ms ms
	 * @param bpm bt/ms
	 * @param ppq tick/qt
	 * @return
	 */
	public static double msToTicks(long ms, float bpm, int ppq) {
		return msToTicks(ms, bpmToMpqn(bpm), ppq);
	}
	/**
	 * bpm:beats per minute!(bt/m)
	 * @param bpm bt/m
	 * @return bt/ms
	 */
	public static int bpmToMpqn(float bpm) {
		return (int)(bpm * 60000000);
	}
	/**
	 * beat per minute
	 * @param mpqn ms per quarter note
	 * @return
	 */
	public static float mpqnToBpm(int mpqn) {
		return mpqn / 60000000.0f;
	}
	
	/**
	 * Utility methods for working with bytes and byte buffers
	 */
	public static int bytesToInt(byte[] buff, int off, int len) {
		
		int num = 0;
		
		int shift = 0;
		for(int i = off + len -1; i >= off; i--) {
			
			num += (buff[i] & 0xFF) << shift;
			shift += 8;
		}
		
		return num;
	}
	/**
	 * Transform the int to byte array
	 * @param val The int value
	 * @param byteCount The count of byte to extract
	 * @return
	 */
	public static byte[] intToBytes(int val, int byteCount) {
		
		byte[] buffer = new byte[byteCount];
		
		int[] ints = new int[byteCount];

		for(int i = 0; i < byteCount; i++) {
			ints[i] = val & 0xFF;
			buffer[byteCount - i - 1] = (byte)ints[i];
			
			val = val >> 8;
		
			if(val == 0) {
				break;
			}
		}
		
		return buffer;
	}
	/**
	 * Judge where the buf1 is equal to buf2 from off to len </br>
	 * for(int i = off; i < off + len; i++) {</br>
	 *		if(i >= buf1.length || i >= buf2.length) {</br>
	 *			return false;</br>
	 *		}</br>
	 *		if(buf1[i] != buf2[i]) {</br>
	 *			return false;</br>
	 *		}</br>
	 *	}</br>
	 *	return true;</br>
	 * @param buf1 a byte array
	 * @param buf2 a byte array
	 * @param off begin of the two byte array
	 * @param len length of the two byte array to compare
	 * @return
	 */
	public static boolean bytesEqual(byte[] buf1, byte[] buf2, int off, int len) {
		
		for(int i = off; i < off + len; i++) {
			if(i >= buf1.length || i >= buf2.length) {
				return false;
			}
			if(buf1[i] != buf2[i]) {
				return false;
			}
		}
		return true;
	}
	/**
	 * extract len bytes from buffer begin at offset
	 * @param buffer src byte array
	 * @param offset The start location
	 * @param len The length to extract
	 * @return
	 */
	public static byte[] extractBytes(byte[] buffer, int offset, int len) {
		
		byte[] ret = new byte[len];
		
		for(int i = 0; i < len; i++) {
			ret[i] = buffer[offset + i];
		}
		
		return ret;
	}
	
	private static final String HEX = "0123456789ABCDEF";
	public static String byteToHex(byte b) {
		
		int high = (b & 0xF0) >> 4;
		int low  = (b & 0x0F);
		
		return "" + HEX.charAt(high) + HEX.charAt(low);
	}
	public static String bytesToHex(byte[] b) {
		StringBuilder sb = new StringBuilder();
		for(int i = 0; i < b.length; i++) {
			sb.append(byteToHex(b[i])).append(" ");
		}
		return sb.toString();
	}
	/**
	 * add two byte array,a+b
	 * @param a
	 * @param b
	 * @return
	 */
	public static byte[] addByte(byte[] a,byte[] b){
		int l=a.length+b.length;
		int i=0;
		byte[] n=new byte[l];
		for(i=0;i<a.length;i++)n[i]=a[i];
		for(;i<l;i++)n[i]=b[i-a.length];
		return n;
	}
	public static byte[] addByte(byte a,byte[] b){
		byte[] c=new byte[]{a};
		int l=c.length+b.length;
		int i=0;
		byte[] n=new byte[l];
		for(i=0;i<c.length;i++)n[i]=c[i];
		for(;i<l;i++)n[i]=b[i-c.length];
		return n;
	}
}
